home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-22 | 64.1 KB | 2,601 lines |
- This is a new archive version of TRN at patchlevel 3.
- The original posting took up Volume23, issues 60 to 73, with
- various problems. These files replace those issues.
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: intrp.c mt-process.c
- # Wrapped by rsalz@litchi.bbn.com on Fri Aug 23 16:38:54 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 5 (of 14)."'
- if test -f 'intrp.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'intrp.c'\"
- else
- echo shar: Extracting \"'intrp.c'\" \(26891 characters\)
- sed "s/^X//" >'intrp.c' <<'END_OF_FILE'
- X/* $Header: intrp.c,v 4.3.3.3 91/01/16 02:43:42 davison Trn $
- X *
- X * $Log: intrp.c,v $
- X * Revision 4.3.3.3 91/01/16 02:43:42 davison
- X * Integrated rn patches 48-54.
- X *
- X * Revision 4.3.3.2 90/08/20 16:29:08 davison
- X * Added HOSTFILE handling. Add OURDOMAIN if site has no '.'
- X *
- X * Revision 4.3.3.1 90/07/21 20:20:13 davison
- X * Initial Trn Release
- X *
- X * Revision 4.3.2.11 90/12/31 11:47:44 sob
- X * NEWSADMIN could not cancel articles because it was not getting set.
- X *
- X * Revision 4.3.2.10 90/12/31 00:02:55 sob
- X * Moved HIDDENET to remove unneeded cruft.
- X *
- X * Revision 4.3.2.9 90/12/30 03:48:11 sob
- X * Changed "hidden" to "hiddennet" to be like nntp and bnews.
- X * Made it possible to cancel articles if hiddennet is defined.
- X *
- X * Revision 4.3.2.8 90/11/22 13:52:27 sob
- X * Made changes to keep preprocessors from complaining.
- X *
- X * Revision 4.3.2.7 90/11/05 23:59:33 sob
- X * moved the definition of tmpbuf such that it get defined before it is used.
- X *
- X * Revision 4.3.2.6 90/11/03 18:52:31 sob
- X * Fixed bug in the definition of the nodename using the uname() system call.
- X *
- X * Revision 4.3.2.5 90/10/01 01:31:18 sob
- X * Fixed problem with struct utsname reported by jrallen@devildog.att.com
- X * when rn is compiled on the Amdahl 5890 UTS 2.0 system.
- X *
- X * Revision 4.3.2.4 90/04/23 00:31:20 sob
- X * Removed unneeded atoi call.
- X *
- X * Revision 4.3.2.3 90/03/22 23:04:35 sob
- X * Fixes provided by Wayne Davison <drivax!davison>
- X *
- X * Revision 4.3.2.2 90/03/17 17:03:12 sob
- X * Fixed determination of the news superuser's id. Fix provided by Chip
- X * Rosenthal <chip@chinacat.lonestar.org>.
- X *
- X * Revision 4.3.2.1 89/12/17 02:54:55 sob
- X * Removed redundant include directive.
- X *
- X * Revision 4.3.1.5 85/05/23 17:21:24 lwall
- X * Now allows 'r' and 'f' on null articles.
- X *
- X * Revision 4.3.1.4 85/05/21 13:35:21 lwall
- X * Sped up "rn -c" by not doing unnecessary initialization.
- X *
- X * Revision 4.3.1.3 85/05/17 10:37:11 lwall
- X * Fixed & substitution to capitalize last name too.
- X *
- X * Revision 4.3.1.2 85/05/15 14:39:45 lwall
- X * Spelled gecos right.
- X *
- X * Revision 4.3.1.1 85/05/10 11:33:51 lwall
- X * Branch for patches.
- X *
- X * Revision 4.3 85/05/01 11:40:54 lwall
- X * Baseline for release with 4.3bsd.
- X *
- X */
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "util.h"
- X#include "search.h"
- X#include "head.h"
- X#include "rn.h"
- X#include "artsrch.h"
- X#include "ng.h"
- X#include "respond.h"
- X#include "rcstuff.h"
- X#include "bits.h"
- X#include "artio.h"
- X#include "term.h"
- X#include "final.h"
- X#ifdef USETHREADS
- X#include "rthreads.h"
- X#endif
- X#include "INTERN.h"
- X#include "intrp.h"
- X
- Xchar orgname[] = ORGNAME;
- X
- X/* name of this site */
- X#ifndef HIDDENNET
- X#ifdef GETHOSTNAME
- X char *hostname;
- X# undef SITENAME
- X# define SITENAME hostname
- X#else /* !GETHOSTNAME */
- X# ifdef DOUNAME
- X# include <sys/utsname.h>
- X struct utsname utsn;
- X# undef SITENAME
- X# define SITENAME utsn.nodename
- X# else /* !DOUNAME */
- X# ifdef PHOSTNAME
- X char *hostname;
- X# undef SITENAME
- X# define SITENAME hostname
- X# else /* !PHOSTNAME */
- X# ifdef WHOAMI
- X# undef SITENAME
- X# define SITENAME sysname
- X# endif /* WHOAMI */
- X# endif /* PHOSTNAME */
- X# endif /* DOUNAME */
- X#endif /* GETHOSTNAME */
- X#endif /*HIDDENNET */
- X
- X#ifdef TILDENAME
- Xstatic char *tildename = Nullch;
- Xstatic char *tildedir = Nullch;
- X#endif
- X
- Xchar *realname INIT(Nullch); /* real name of sender from /etc/passwd */
- X
- X#ifdef CONDSUB
- Xchar *skipinterp ANSI((char *,char *));
- X#endif
- X
- Xstatic void abort_interp ANSI((void));
- X
- Xvoid
- Xintrp_init(tcbuf)
- Xchar *tcbuf;
- X{
- X char *getlogin();
- X
- X spool = savestr(filexp(SPOOL)); /* usually /usr/spool/news */
- X
- X /* get environmental stuff */
- X
- X#ifdef NEWSADMIN
- X#ifdef GETPWENT
- X {
- X struct passwd *getpwnam();
- X struct passwd *pwd = getpwnam(NEWSADMIN);
- X
- X if (pwd != NULL)
- X newsuid = pwd->pw_uid;
- X }
- X#endif /* GETPWENT */
- X#endif /* NEWSADMIN */
- X /* get home directory */
- X
- X homedir = getenv("HOME");
- X if (homedir == Nullch)
- X homedir = getenv("LOGDIR");
- X
- X dotdir = getval("DOTDIR",homedir);
- X
- X /* get login name */
- X
- X logname = getenv("USER");
- X if (logname == Nullch)
- X logname = getenv("LOGNAME");
- X#ifdef GETLOGIN
- X if (logname == Nullch)
- X logname = savestr(getlogin());
- X#endif
- X
- X#ifdef NEWSADMIN
- X /* if this is the news admin than load his UID into newsuid */
- X
- X if ( strEQ(logname,NEWSADMIN) )
- X newsuid = getuid();
- X#endif
- X
- X if (checkflag) /* that getwd below takes ~1/3 sec. */
- X return; /* and we do not need it for -c */
- X getwd(tcbuf); /* find working directory name */
- X origdir = savestr(tcbuf); /* and remember it */
- X
- X /* get the real name of the person (%N) */
- X /* Must be done after logname is read in because BERKNAMES uses that */
- X
- X strcpy(tcbuf,getrealname(getuid()));
- X realname = savestr(tcbuf);
- X
- X /* name of header file (%h) */
- X
- X headname = savestr(filexp(HEADNAME));
- X
- X /* name of this site (%H) */
- X
- X#ifndef HIDDENNET
- X#ifdef HOSTFILE
- X if ((tmpfp = fopen(HOSTFILE,"r")) == NULL) {
- X hostname = "unknown";
- X printf("Warning: Couldn't open %s to determine hostname!\n", HOSTFILE);
- X } else {
- X fgets(buf, sizeof(buf), tmpfp);
- X buf[strlen(buf)-1] = 0;
- X hostname = savestr(buf);
- X fclose(tmpfp);
- X }
- X#else
- X#ifdef GETHOSTNAME
- X gethostname(buf,sizeof buf);
- X hostname = savestr(buf);
- X#else
- X#ifdef DOUNAME
- X /* get sysname */
- X uname(&utsn);
- X#else
- X#ifdef PHOSTNAME
- X {
- X FILE *popen();
- X FILE *pipefp = popen(PHOSTNAME,"r");
- X
- X if (pipefp == Nullfp) {
- X printf("Can't find hostname\n");
- X sig_catcher(0);
- X }
- X fgets(buf,sizeof buf,pipefp);
- X buf[strlen(buf)-1] = '\0'; /* wipe out newline */
- X hostname = savestr(buf);
- X pclose(pipefp);
- X }
- X#endif /* PHOSTNAME */
- X#endif /* DOUNAME */
- X#endif /* GETHOSTNAME */
- X#endif /* HOSTFILE */
- X if (index(SITENAME,'.') == NULL) {
- X sprintf(buf, "%s.%s", SITENAME, OURDOMAIN);
- X sitename = savestr(buf);
- X } else
- X sitename = savestr(SITENAME);
- X#else
- X sitename = savestr(OURDOMAIN);
- X#endif
- X}
- X
- X/* expand filename via %, ~, and $ interpretation */
- X/* returns pointer to static area */
- X/* Note that there is a 1-deep cache of ~name interpretation */
- X
- Xchar *
- Xfilexp(s)
- Xregister char *s;
- X{
- X static char filename[CBUFLEN];
- X char scrbuf[CBUFLEN];
- X register char *d;
- X
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("< %s\n",s) FLUSH;
- X#endif
- X interp(filename, (sizeof filename), s); /* interpret any % escapes */
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("%% %s\n",filename) FLUSH;
- X#endif
- X s = filename;
- X if (*s == '~') { /* does destination start with ~? */
- X if (!*(++s) || *s == '/') {
- X sprintf(scrbuf,"%s%s",homedir,s);
- X /* swap $HOME for it */
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("~ %s\n",scrbuf) FLUSH;
- X#endif
- X strcpy(filename,scrbuf);
- X }
- X else {
- X#ifdef TILDENAME
- X for (d=scrbuf; isalnum(*s); s++,d++)
- X *d = *s;
- X *d = '\0';
- X if (tildedir && strEQ(tildename,scrbuf)) {
- X strcpy(scrbuf,tildedir);
- X strcat(scrbuf, s);
- X strcpy(filename, scrbuf);
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("r %s %s\n",tildename,tildedir) FLUSH;
- X#endif
- X }
- X else {
- X if (tildename) {
- X free(tildename);
- X free(tildedir);
- X }
- X tildedir = Nullch;
- X tildename = savestr(scrbuf);
- X#ifdef GETPWENT /* getpwnam() is not the paragon of efficiency */
- X {
- X struct passwd *getpwnam();
- X struct passwd *pwd = getpwnam(tildename);
- X
- X sprintf(scrbuf,"%s%s",pwd->pw_dir,s);
- X tildedir = savestr(pwd->pw_dir);
- X strcpy(filename,scrbuf);
- X#ifdef GETPWENT
- X endpwent();
- X#endif
- X }
- X#else /* this will run faster, and is less D space */
- X { /* just be sure LOGDIRFIELD is correct */
- X FILE *pfp = fopen("/etc/passwd","r");
- X char tmpbuf[512];
- X int i;
- X
- X if (pfp == Nullfp) {
- X printf(cantopen,"passwd") FLUSH;
- X sig_catcher(0);
- X }
- X while (fgets(tmpbuf,512,pfp) != Nullch) {
- X d = cpytill(scrbuf,tmpbuf,':');
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("p %s\n",tmpbuf) FLUSH;
- X#endif
- X if (strEQ(scrbuf,tildename)) {
- X for (i=LOGDIRFIELD-2; i; i--) {
- X if (d)
- X d = index(d+1,':');
- X }
- X if (d) {
- X cpytill(scrbuf,d+1,':');
- X tildedir = savestr(scrbuf);
- X strcat(scrbuf,s);
- X strcpy(filename,scrbuf);
- X }
- X break;
- X }
- X }
- X fclose(pfp);
- X }
- X#endif
- X }
- X#else /* !TILDENAME */
- X#ifdef VERBOSE
- X IF(verbose)
- X fputs("~loginname not implemented.\n",stdout) FLUSH;
- X ELSE
- X#endif
- X#ifdef TERSE
- X fputs("~login not impl.\n",stdout) FLUSH;
- X#endif
- X#endif
- X }
- X }
- X else if (*s == '$') { /* starts with some env variable? */
- X d = scrbuf;
- X *d++ = '%';
- X if (s[1] == '{')
- X strcpy(d,s+2);
- X else {
- X *d++ = '{';
- X for (s++; isalnum(*s); s++) *d++ = *s;
- X /* skip over token */
- X *d++ = '}';
- X strcpy(d,s);
- X }
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("$ %s\n",scrbuf) FLUSH;
- X#endif
- X interp(filename, (sizeof filename), scrbuf);
- X /* this might do some extra '%'s but */
- X /* that is how the Mercedes Benz */
- X }
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X printf("> %s\n",filename) FLUSH;
- X#endif
- X return filename;
- X}
- X
- X#ifdef CONDSUB
- X/* skip interpolations */
- X
- Xchar *
- Xskipinterp(pattern,stoppers)
- Xregister char *pattern;
- Xchar *stoppers;
- X{
- X
- X while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
- X#ifdef DEBUGGING
- X if (debug & 8)
- X printf("skipinterp till %s at %s\n",stoppers?stoppers:"",pattern);
- X#endif
- X if (*pattern == '%' && pattern[1]) {
- X switch (*++pattern) {
- X case '{':
- X for (pattern++; *pattern && *pattern != '}'; pattern++)
- X if (*pattern == '\\')
- X pattern++;
- X break;
- X case '[':
- X for (pattern++; *pattern && *pattern != ']'; pattern++)
- X if (*pattern == '\\')
- X pattern++;
- X break;
- X#ifdef CONDSUB
- X case '(': {
- X pattern = skipinterp(pattern+1,"!=");
- X if (!*pattern)
- X goto getout;
- X for (pattern++; *pattern && *pattern != '?'; pattern++)
- X if (*pattern == '\\')
- X pattern++;
- X if (!*pattern)
- X goto getout;
- X pattern = skipinterp(pattern+1,":)");
- X if (*pattern == ':')
- X pattern = skipinterp(pattern+1,")");
- X break;
- X }
- X#endif
- X#ifdef BACKTICK
- X case '`': {
- X pattern = skipinterp(pattern+1,"`");
- X break;
- X }
- X#endif
- X#ifdef PROMPTTTY
- X case '"':
- X pattern = skipinterp(pattern+1,"\"");
- X break;
- X#endif
- X default:
- X break;
- X }
- X pattern++;
- X }
- X else {
- X if (*pattern == '^' && pattern[1])
- X pattern += 2;
- X else if (*pattern == '\\' && pattern[1])
- X pattern += 2;
- X else
- X pattern++;
- X }
- X }
- Xgetout:
- X return pattern; /* where we left off */
- X}
- X#endif
- X
- X/* interpret interpolations */
- X
- Xchar *
- Xdointerp(dest,destsize,pattern,stoppers)
- Xregister char *dest;
- Xregister int destsize;
- Xregister char *pattern;
- Xchar *stoppers;
- X{
- X char *subj_buf = Nullch;
- X char *ngs_buf = Nullch;
- X char *refs_buf = Nullch;
- X char *artid_buf = Nullch;
- X char *reply_buf = Nullch;
- X char *from_buf = Nullch;
- X char *path_buf = Nullch;
- X char *follow_buf = Nullch;
- X char *dist_buf = Nullch;
- X char *line_buf = Nullch;
- X register char *s, *h;
- X register int i;
- X char scrbuf[512];
- X bool upper = FALSE;
- X bool lastcomp = FALSE;
- X int metabit = 0;
- X
- X while (*pattern && (!stoppers || !index(stoppers,*pattern))) {
- X#ifdef DEBUGGING
- X if (debug & 8)
- X printf("dointerp till %s at %s\n",stoppers?stoppers:"",pattern);
- X#endif
- X if (*pattern == '%' && pattern[1]) {
- X upper = FALSE;
- X lastcomp = FALSE;
- X for (s=Nullch; !s; ) {
- X switch (*++pattern) {
- X case '^':
- X upper = TRUE;
- X break;
- X case '_':
- X lastcomp = TRUE;
- X break;
- X case '/':
- X#ifdef ARTSRCH
- X s = scrbuf;
- X if (!index("/?g",pattern[-2]))
- X *s++ = '/';
- X strcpy(s,lastpat);
- X s += strlen(s);
- X if (pattern[-2] != 'g') {
- X if (index("/?",pattern[-2]))
- X *s++ = pattern[-2];
- X else
- X *s++ = '/';
- X if (art_howmuch == 1)
- X *s++ = 'h';
- X else if (art_howmuch == 2)
- X *s++ = 'a';
- X if (art_doread)
- X *s++ = 'r';
- X }
- X *s = '\0';
- X s = scrbuf;
- X#else
- X s = nullstr;
- X#endif
- X break;
- X case '{':
- X pattern = cpytill(scrbuf,pattern+1,'}');
- X if (s = index(scrbuf,'-'))
- X *s++ = '\0';
- X else
- X s = nullstr;
- X s = getval(scrbuf,s);
- X break;
- X case '[':
- X pattern = cpytill(scrbuf,pattern+1,']');
- X i = set_line_type(scrbuf,scrbuf+strlen(scrbuf));
- X if (line_buf)
- X free(line_buf);
- X s = line_buf = fetchlines(art,i);
- X break;
- X#ifdef CONDSUB
- X case '(': {
- X COMPEX *oldbra_compex = bra_compex;
- X COMPEX cond_compex;
- X char rch;
- X bool matched;
- X
- X init_compex(&cond_compex);
- X pattern = dointerp(dest,destsize,pattern+1,"!=");
- X rch = *pattern;
- X if (rch == '!')
- X pattern++;
- X if (*pattern != '=')
- X goto getout;
- X pattern = cpytill(scrbuf,pattern+1,'?');
- X if (!*pattern)
- X goto getout;
- X if (s = compile(&cond_compex,scrbuf,TRUE,TRUE)) {
- X printf("%s: %s\n",scrbuf,s) FLUSH;
- X pattern += strlen(pattern);
- X goto getout;
- X }
- X matched = (execute(&cond_compex,dest) != Nullch);
- X if (cond_compex.nbra) /* were there brackets? */
- X bra_compex = &cond_compex;
- X if (matched==(rch == '=')) {
- X pattern = dointerp(dest,destsize,pattern+1,":)");
- X if (*pattern == ':')
- X pattern = skipinterp(pattern+1,")");
- X }
- X else {
- X pattern = skipinterp(pattern+1,":)");
- X if (*pattern == ':')
- X pattern++;
- X pattern = dointerp(dest,destsize,pattern,")");
- X }
- X s = dest;
- X bra_compex = oldbra_compex;
- X free_compex(&cond_compex);
- X break;
- X }
- X#endif
- X#ifdef BACKTICK
- X case '`': {
- X FILE *pipefp, *popen();
- X
- X pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"`");
- X pipefp = popen(scrbuf,"r");
- X if (pipefp != Nullfp) {
- X int len;
- X
- X len = fread(scrbuf,sizeof(char),(sizeof scrbuf)-1,
- X pipefp);
- X scrbuf[len] = '\0';
- X pclose(pipefp);
- X }
- X else {
- X printf("\nCan't run %s\n",scrbuf);
- X *scrbuf = '\0';
- X }
- X for (s=scrbuf; *s; s++) {
- X if (*s == '\n') {
- X if (s[1])
- X *s = ' ';
- X else
- X *s = '\0';
- X }
- X }
- X s = scrbuf;
- X break;
- X }
- X#endif
- X#ifdef PROMPTTTY
- X case '"':
- X pattern = dointerp(scrbuf,(sizeof scrbuf),pattern+1,"\"");
- X fputs(scrbuf,stdout) FLUSH;
- X resetty();
- X gets(scrbuf);
- X noecho();
- X crmode();
- X s = scrbuf;
- X break;
- X#endif
- X case '~':
- X s = homedir;
- X break;
- X case '.':
- X s = dotdir;
- X break;
- X case '$':
- X s = scrbuf;
- X sprintf(s,"%d",getpid());
- X break;
- X case '#':
- X s = scrbuf;
- X sprintf(s,"%d",perform_cnt);
- X break;
- X case '0': case '1': case '2': case '3': case '4':
- X case '5': case '6': case '7': case '8': case '9':
- X#ifdef CONDSUB
- X s = getbracket(bra_compex,*pattern - '0');
- X#else
- X s = nullstr;
- X#endif
- X break;
- X case 'a':
- X s = scrbuf;
- X sprintf(s,"%ld",(long)art);
- X break;
- X case 'A':
- X#ifdef LINKART
- X s = linkartname; /* so Eunice people get right file */
- X#else
- X s = scrbuf;
- X sprintf(s,"%s/%s/%ld",spool,ngdir,(long)art);
- X#endif
- X break;
- X case 'b':
- X s = savedest;
- X break;
- X case 'B':
- X s = scrbuf;
- X sprintf(s,"%ld",(long)savefrom);
- X break;
- X case 'c':
- X s = ngdir;
- X break;
- X case 'C':
- X s = ngname;
- X break;
- X case 'd':
- X s = scrbuf;
- X sprintf(s,"%s/%s",spool,ngdir);
- X break;
- X case 'D':
- X s = dist_buf = fetchlines(art,DIST_LINE);
- X break;
- X case 'e':
- X s = extractprog;
- X break;
- X#ifdef USETHREADS
- X case 'E': {
- X int sel, unseen;
- X
- X sel = curr_p_art && (selected_roots[curr_p_art->root] & 1);
- X unseen = (art <= lastart) && !was_read(art);
- X sprintf(scrbuf,"%ld",(long)toread[ng]-selected_count
- X -unthreaded-(!sel && unseen));
- X s = scrbuf;
- X break;
- X }
- X#endif
- X case 'f': /* from line */
- X#ifdef ASYNC_PARSE
- X parse_maybe(art);
- X#endif
- X if (htype[REPLY_LINE].ht_minpos >= 0) {
- X /* was there a reply line? */
- X if (!(s=reply_buf))
- X s = reply_buf = fetchlines(art,REPLY_LINE);
- X }
- X else if (!(s = from_buf))
- X s = from_buf = fetchlines(art,FROM_LINE);
- X break;
- X case 'F':
- X#ifdef ASYNC_PARSE
- X parse_maybe(art);
- X#endif
- X if (htype[FOLLOW_LINE].ht_minpos >= 0)
- X /* is there a Followup-To line? */
- X s = follow_buf = fetchlines(art,FOLLOW_LINE);
- X else {
- X int off;
- X
- X s = ngs_buf = fetchlines(art,NGS_LINE);
- X if (h = instr(s,"net.general")) {
- X off = h-s;
- X strncpy(scrbuf,s,off+4);
- X strcpy(scrbuf+off+4,"followup");
- X safecpy(scrbuf+off+12,h+11,sizeof(scrbuf));
- X s = scrbuf;
- X }
- X }
- X break;
- X case 'h': /* header file name */
- X s = headname;
- X break;
- X case 'H': /* host name */
- X s = sitename;
- X break;
- X case 'i':
- X if (!(s=artid_buf))
- X s = artid_buf = fetchlines(art,MESSID_LINE);
- X if (*s && *s != '<') {
- X sprintf(scrbuf,"<%s>",artid_buf);
- X s = scrbuf;
- X }
- X break;
- X case 'I': /* ref article indicator */
- X s = scrbuf;
- X sprintf(scrbuf,"'%s'",indstr);
- X break;
- X case 'l': /* rn library */
- X#ifdef NEWSADMIN
- X s = newsadmin;
- X#else
- X s = "???";
- X#endif
- X break;
- X case 'L': /* login id */
- X s = logname;
- X break;
- X case 'm': /* current mode */
- X s = scrbuf;
- X *s = mode;
- X s[1] = '\0';
- X break;
- X case 'M':
- X#ifdef DELAYMARK
- X sprintf(scrbuf,"%ld",(long)dmcount);
- X s = scrbuf;
- X#else
- X s = nullstr;
- X#endif
- X break;
- X case 'n': /* newsgroups */
- X s = ngs_buf = fetchlines(art,NGS_LINE);
- X break;
- X case 'N': /* full name */
- X s = getval("NAME",realname);
- X break;
- X case 'o': /* organization */
- X s = getval("ORGANIZATION",orgname);
- X#ifdef ORGFILE
- X if (*s == '/') {
- X FILE *ofp = fopen(s,"r");
- X
- X if (ofp) {
- X fgets(scrbuf,sizeof scrbuf,ofp);
- X fclose(ofp);
- X s = scrbuf;
- X s[strlen(s)-1] = '\0';
- X }
- X }
- X#endif
- X break;
- X case 'O':
- X s = origdir;
- X break;
- X case 'p':
- X s = cwd;
- X break;
- X case 'P':
- X s = spool;
- X break;
- X case 'r':
- X#ifdef ASYNC_PARSE
- X parse_maybe(art);
- X#endif
- X if (htype[REFS_LINE].ht_minpos >= 0) {
- X refs_buf = fetchlines(art,REFS_LINE);
- X refscpy(scrbuf,(sizeof scrbuf),refs_buf);
- X }
- X else
- X *scrbuf = '\0';
- X s = rindex(scrbuf,'<');
- X break;
- X case 'R':
- X#ifdef ASYNC_PARSE
- X parse_maybe(art);
- X#endif
- X if (htype[REFS_LINE].ht_minpos >= 0) {
- X refs_buf = fetchlines(art,REFS_LINE);
- X refscpy(scrbuf,(sizeof scrbuf),refs_buf);
- X /* no more than 3 prior references allowed,
- X ** including the one concatenated below */
- X if ((s = rindex(scrbuf,'<')) > scrbuf) {
- X *s = '\0';
- X h = rindex(scrbuf,'<');
- X *s = '<';
- X if (h > scrbuf)
- X strcpy(scrbuf,h);
- X }
- X }
- X else
- X *scrbuf = '\0';
- X if (!artid_buf)
- X artid_buf = fetchlines(art,MESSID_LINE);
- X if (artid_buf[0] == '<')
- X safecat(scrbuf,artid_buf,sizeof(scrbuf));
- X else if (artid_buf[0]) {
- X char tmpbuf[64];
- X
- X sprintf(tmpbuf,"<%s>",artid_buf);
- X safecat(scrbuf,tmpbuf,sizeof(scrbuf));
- X }
- X s = scrbuf;
- X break;
- X case 's':
- X if (!(s=subj_buf))
- X s = subj_buf = fetchsubj(art,TRUE,TRUE);
- X /* get subject handy */
- X while ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
- X /* skip extra Re: */
- X s += 3;
- X if (*s == ' ')
- X s++;
- X }
- X if (h = instr(s,"- (nf"))
- X *h = '\0';
- X break;
- X case 'S':
- X if (!(s=subj_buf))
- X s = subj_buf = fetchsubj(art,TRUE,TRUE);
- X /* get subject handy */
- X if ((*s=='R'||*s=='r')&&(s[1]=='E'||s[1]=='e')&&s[2]==':') {
- X /* skip extra Re: */
- X s += 3;
- X if (*s == ' ')
- X s++;
- X }
- X break;
- X case 't':
- X case 'T':
- X#ifdef ASYNC_PARSE
- X parse_maybe(art);
- X#endif
- X if (htype[REPLY_LINE].ht_minpos >= 0) {
- X /* was there a reply line? */
- X if (!(s=reply_buf))
- X s = reply_buf = fetchlines(art,REPLY_LINE);
- X }
- X else if (!(s = from_buf))
- X s = from_buf = fetchlines(art,FROM_LINE);
- X if (*pattern == 'T') {
- X if (htype[PATH_LINE].ht_minpos >= 0) {
- X /* should we substitute path? */
- X s = path_buf = fetchlines(art,PATH_LINE);
- X }
- X i = strlen(sitename);
- X if (strnEQ(sitename,s,i) && s[i] == '!')
- X s += i + 1;
- X }
- X if ((h=index(s,'(')) != Nullch)
- X /* strip garbage from end */
- X *(h-1) = '\0';
- X else if ((h=index(s,'<')) != Nullch) {
- X /* or perhaps from beginning */
- X s = h+1;
- X if ((h=index(s,'>')) != Nullch)
- X *h = '\0';
- X }
- X break;
- X case 'u':
- X sprintf(scrbuf,"%ld",(long)toread[ng]);
- X s = scrbuf;
- X break;
- X case 'U': {
- X int unseen;
- X
- X unseen = (art <= lastart) && !was_read(art);
- X#ifdef USETHREADS
- X if (selected_root_cnt) {
- X int sel;
- X
- X sel = curr_p_art
- X && (selected_roots[curr_p_art->root] & 1);
- X sprintf(scrbuf,"%ld",
- X (long)selected_count-(sel && unseen));
- X }
- X else
- X sprintf(scrbuf,"%ld",(long)toread[ng]-unthreaded
- X -unseen);
- X#else
- X sprintf(scrbuf,"%ld",(long)toread[ng]-unseen);
- X#endif
- X s = scrbuf;
- X break;
- X }
- X case 'x': /* news library */
- X s = lib;
- X break;
- X case 'X': /* rn library */
- X s = rnlib;
- X break;
- X case 'z':
- X#ifdef LINKART
- X s = linkartname; /* so Eunice people get right file */
- X#else
- X s = scrbuf;
- X sprintf(s,"%ld",(long)art);
- X#endif
- X if (stat(s,&filestat) < 0)
- X filestat.st_size = 0L;
- X sprintf(scrbuf,"%5ld",(long)filestat.st_size);
- X s = scrbuf;
- X break;
- X#ifdef USETHREADS
- X case 'Z':
- X sprintf(scrbuf,"%ld",(long)selected_count);
- X s = scrbuf;
- X break;
- X#endif
- X default:
- X if (--destsize <= 0)
- X abort_interp();
- X *dest++ = *pattern | metabit;
- X s = nullstr;
- X break;
- X }
- X }
- X if (!s)
- X s = nullstr;
- X pattern++;
- X if (upper || lastcomp) {
- X char *t;
- X
- X if (s != scrbuf) {
- X safecpy(scrbuf,s,(sizeof scrbuf));
- X s = scrbuf;
- X }
- X if (upper || !(t=rindex(s,'/')))
- X t = s;
- X while (*t && !isalpha(*t))
- X t++;
- X if (islower(*t))
- X *t = toupper(*t);
- X }
- X i = metabit; /* maybe get into register */
- X if (s == dest) {
- X while (*dest) {
- X if (--destsize <= 0)
- X abort_interp();
- X *dest++ |= i;
- X }
- X }
- X else {
- X while (*s) {
- X if (--destsize <= 0)
- X abort_interp();
- X *dest++ = *s++ | i;
- X }
- X }
- X }
- X else {
- X if (--destsize <= 0)
- X abort_interp();
- X if (*pattern == '^' && pattern[1]) {
- X ++pattern; /* skip uparrow */
- X i = *pattern; /* get char into a register */
- X if (i == '?')
- X *dest++ = '\177' | metabit;
- X else if (i == '(') {
- X metabit = 0200;
- X destsize++;
- X }
- X else if (i == ')') {
- X metabit = 0;
- X destsize++;
- X }
- X else
- X *dest++ = i & 037 | metabit;
- X pattern++;
- X }
- X else if (*pattern == '\\' && pattern[1]) {
- X ++pattern; /* skip backslash */
- X i = *pattern; /* get char into a register */
- X
- X /* this used to be a switch but the if may save space */
- X
- X if (i >= '0' && i <= '7') {
- X i = 1;
- X while (i < 01000 && *pattern >= '0' && *pattern <= '7') {
- X i <<= 3;
- X i += *pattern++ - '0';
- X }
- X *dest++ = i & 0377 | metabit;
- X --pattern;
- X }
- X else if (i == 'b')
- X *dest++ = '\b' | metabit;
- X else if (i == 'f')
- X *dest++ = '\f' | metabit;
- X else if (i == 'n')
- X *dest++ = '\n' | metabit;
- X else if (i == 'r')
- X *dest++ = '\r' | metabit;
- X else if (i == 't')
- X *dest++ = '\t' | metabit;
- X else
- X *dest++ = i | metabit;
- X pattern++;
- X }
- X else
- X *dest++ = *pattern++ | metabit;
- X }
- X }
- X *dest = '\0';
- Xgetout:
- X if (subj_buf != Nullch) /* return any checked out storage */
- X free(subj_buf);
- X if (ngs_buf != Nullch)
- X free(ngs_buf);
- X if (refs_buf != Nullch)
- X free(refs_buf);
- X if (artid_buf != Nullch)
- X free(artid_buf);
- X if (reply_buf != Nullch)
- X free(reply_buf);
- X if (from_buf != Nullch)
- X free(from_buf);
- X if (path_buf != Nullch)
- X free(path_buf);
- X if (follow_buf != Nullch)
- X free(follow_buf);
- X if (dist_buf != Nullch)
- X free(dist_buf);
- X if (line_buf != Nullch)
- X free(line_buf);
- X return pattern; /* where we left off */
- X}
- X
- Xvoid
- Xinterp(dest,destsize,pattern)
- Xchar *dest;
- Xint destsize;
- Xchar *pattern;
- X{
- X dointerp(dest,destsize,pattern,Nullch);
- X#ifdef DEBUGGING
- X if (debug & DEB_FILEXP)
- X fputs(dest,stdout);
- X#endif
- X}
- X
- X/* copy a references line, normalizing as we go */
- X
- Xvoid
- Xrefscpy(dest,destsize,src)
- Xregister char *dest, *src;
- Xregister int destsize;
- X{
- X register char *dot, *at, *beg;
- X char tmpbuf[64];
- X
- X while (*src) {
- X if (*src != '<') {
- X if (--destsize <= 0)
- X break;
- X *dest++ = '<';
- X at = dot = Nullch;
- X beg = src;
- X while (*src && *src != ' ' && *src != ',') {
- X if (*src == '.')
- X dot = src;
- X else if (*src == '@')
- X at = src;
- X if (--destsize <= 0)
- X break;
- X *dest++ = *src++;
- X }
- X if (destsize <= 0)
- X break;
- X if (dot && !at) {
- X int len;
- X
- X *dest = *dot++ = '\0';
- X sprintf(tmpbuf,"%s@%s.UUCP",dot,beg);
- X len = strlen(tmpbuf);
- X if (destsize > len) {
- X strcpy(dest,tmpbuf);
- X dest = dest + len;
- X destsize -= len;
- X }
- X }
- X if (--destsize <= 0)
- X break;
- X *dest++ = '>';
- X }
- X else {
- X while (*src && --destsize > 0 && (*dest++ = *src++) != '>') ;
- X if (destsize <= 0)
- X break;
- X }
- X while (*src == ' ' || *src == ',') src++;
- X if (*src && --destsize > 0)
- X *dest++ = ' ';
- X }
- X *dest = '\0';
- X}
- X
- X/* get the person's real name from /etc/passwd */
- X/* (string is overwritten, so it must be copied) */
- X
- Xchar *
- Xgetrealname(uid)
- Xint uid;
- X{
- X char *s, *c;
- X char tmpbuf[512];
- X
- X#ifdef PASSNAMES
- X#ifdef GETPWENT
- X struct passwd *pwd = getpwuid(uid);
- X
- X s = pwd->pw_gecos;
- X#else
- X int i;
- X
- X getpw(uid, tmpbuf);
- X for (s=tmpbuf, i=GCOSFIELD-1; i; i--) {
- X if (s)
- X s = index(s,':')+1;
- X }
- X if (!s)
- X return nullstr;
- X cpytill(tmpbuf,s,':');
- X s = tmpbuf;
- X#endif
- X#ifdef BERKNAMES
- X#ifdef BERKJUNK
- X while (*s && !isalnum(*s) && *s != '&') s++;
- X#endif
- X if ((c = index(s, ',')) != Nullch)
- X *c = '\0';
- X if ((c = index(s, ';')) != Nullch)
- X *c = '\0';
- X s = cpytill(buf,s,'&');
- X if (*s == '&') { /* whoever thought this one up was */
- X c = buf + strlen(buf); /* in the middle of the night */
- X strcat(c,logname); /* before the morning after */
- X strcat(c,s+1);
- X if (islower(*c))
- X *c = toupper(*c); /* gack and double gack */
- X }
- X#else
- X if ((c = index(s, '(')) != Nullch)
- X *c = '\0';
- X if ((c = index(s, '-')) != Nullch)
- X s = c;
- X strcpy(buf,tmpbuf);
- X#endif
- X#ifdef GETPWENT
- X endpwent();
- X#endif
- X return buf; /* return something static */
- X#else
- X if ((tmpfp=fopen(filexp(FULLNAMEFILE),"r")) != Nullfp) {
- X fgets(buf,sizeof buf,tmpfp);
- X fclose(tmpfp);
- X buf[strlen(buf)-1] = '\0';
- X return buf;
- X }
- X return "PUT YOUR NAME HERE";
- X#endif
- X}
- X
- Xstatic void
- Xabort_interp()
- X{
- X fputs("\n% interp buffer overflow!\n",stdout) FLUSH;
- X sig_catcher(0);
- X}
- X
- X
- END_OF_FILE
- if test 26891 -ne `wc -c <'intrp.c'`; then
- echo shar: \"'intrp.c'\" unpacked with wrong size!
- fi
- # end of 'intrp.c'
- fi
- if test -f 'mt-process.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'mt-process.c'\"
- else
- echo shar: Extracting \"'mt-process.c'\" \(34306 characters\)
- sed "s/^X//" >'mt-process.c' <<'END_OF_FILE'
- X/* $Header: mt-process.c,v 4.3.3.3 91/01/18 19:13:20 davison Trn $
- X**
- X** $Log: mt-process.c,v $
- X** Revision 4.3.3.3 91/01/18 19:13:20 davison
- X** Removed the code that tried to exclude certain message ids. Added -s option
- X**
- X** Revision 4.3.3.2 90/08/20 16:40:31 davison
- X** Added check of caught_interrupt flag into main loops.
- X**
- X** Revision 4.3.3.1 90/07/28 18:04:45 davison
- X** Initial Trn Release
- X**
- X*/
- X
- X#include "EXTERN.h"
- X#include "common.h"
- X#include "mthreads.h"
- X#ifdef SERVER
- X#include "server.h"
- X#endif
- X
- X#include <time.h>
- X#ifndef TZSET
- X# include <sys/timeb.h>
- X#endif
- X
- Xchar buff[1024];
- X
- Xchar references[1024];
- X
- Xchar subject_str[80];
- Xbool found_Re;
- X
- Xchar author_str[20];
- X
- Xextern int log_verbosity, slow_down;
- X
- XDOMAIN *next_domain;
- X
- Xvoid insert_article(), expire(), trim_roots(), order_roots(), trim_authors();
- Xvoid make_root(), use_root(), merge_roots(), set_root(), unlink_root();
- Xvoid link_child(), unlink_child();
- Xvoid free_article(), free_domain(), free_subject(), free_root(), free_author();
- Xvoid get_subject_str(), get_author_str();
- XARTICLE *get_article();
- XSUBJECT *new_subject();
- XAUTHOR *new_author();
- X
- X#ifdef TZSET
- Xextern time_t tnow;
- X#else
- Xextern struct timeb ftnow;
- X#endif
- X
- X#ifndef SERVER
- Xstatic FILE *fp_article;
- X#endif
- X
- X/* Given the upper/lower bounds of the articles in the current group, add all
- X** the ones that we don't know about and remove all the ones that have expired.
- X** The current directory must be the newgroup's spool directory.
- X*/
- Xvoid
- Xprocess_articles( first_article, last_article )
- XART_NUM first_article, last_article;
- X{
- X register char *cp, *str;
- X register ARTICLE *article;
- X register ART_NUM i;
- X time_t date;
- X int len;
- X#ifdef SERVER
- X bool orig_extra = extra_expire;
- X#endif
- X extern int errno;
- X extern int sys_nerr;
- X extern char *sys_errlist[];
- X
- X if( first_article > (i = total.last+1) ) {
- X i = first_article;
- X }
- X added_count = last_article - i + 1;
- X expired_count = 0;
- X
- X for( ; i <= last_article; i++ ) {
- X if( caught_interrupt ) {
- X return;
- X }
- X#ifdef SERVER
- X if( slow_down ) {
- X sleep( slow_down );
- X }
- X sprintf( buff, "HEAD %ld", (long)i );
- X put_server( buff );
- X if( get_server( buff, sizeof buff ) < 0 || *buff == CHAR_FATAL ) {
- X last_article = i - 1;
- X extra_expire = FALSE;
- X break;
- X }
- X if( *buff != CHAR_OK ) {
- X added_count--;
- X continue;
- X }
- X#else
- X /* Open article in current directory. */
- X sprintf( buff, "%ld", (long)i );
- X /* Set errno for purely paranoid reasons */
- X errno = 0;
- X if( (fp_article = fopen( buff, "r" )) == Nullfp ) {
- X /* Missing files are ok -- they've just been expired or canceled */
- X if( errno != 0 && errno != ENOENT ) {
- X if( errno < 0 || errno > sys_nerr ) {
- X log_error( "Can't open `%s': Error %d.\n", buff, errno );
- X } else {
- X log_error( "Can't open `%s': %s.\n", buff,
- X sys_errlist[errno] );
- X }
- X }
- X added_count--;
- X continue;
- X }
- X#endif
- X
- X article = Nullart;
- X *references = '\0';
- X *author_str = '\0';
- X *subject_str = '\0';
- X found_Re = 0;
- X date = 0;
- X
- X#ifdef SERVER
- X while( get_server( cp = buff, sizeof buff ) == 0 ) {
- X process_line:
- X if( *cp == '.' ) {
- X break;
- X }
- X#else
- X while( (cp = fgets( buff, sizeof buff, fp_article )) != Nullch ) {
- X process_line:
- X if( *cp == '\n' ) { /* check for end of header */
- X break; /* break out when found */
- X }
- X#endif
- X if( (unsigned char)*cp <= ' ' ) { /* skip continuation lines */
- X continue; /* (except references -- see below) */
- X }
- X if( (str = index( cp, ':' )) == Nullch ) {
- X break; /* end of header if no colon found */
- X }
- X if( (len = str - cp) > 10 ) {
- X continue; /* skip keywords > 10 chars */
- X }
- X#ifndef SERVER
- X cp[strlen(cp)-1] = '\0'; /* remove newline */
- X#endif
- X while( cp < str ) { /* lower-case the keyword */
- X if( (unsigned char)*cp <= ' ' ) { /* stop at any whitespace */
- X break;
- X }
- X if( isupper(*cp) ) {
- X *cp = tolower(*cp);
- X }
- X cp++;
- X }
- X *cp = '\0';
- X cp = buff;
- X if( len == 4 && strEQ( cp, "date" ) ) {
- X#ifdef TZSET
- X date = getdate( str + 1, tnow, timezone );
- X#else
- X date = getdate( str + 1, ftnow.time, (long) ftnow.timezone );
- X#endif
- X } else
- X if( len == 4 && strEQ( cp, "from" ) ) {
- X get_author_str( str + 1 );
- X } else
- X if( len == 7 && strEQ( cp, "subject" ) ) {
- X get_subject_str( str + 1 );
- X } else
- X if( len == 10 && strEQ( cp, "message-id" ) ) {
- X if( !article ) {
- X article = get_article( str + 1 );
- X } else {
- X if( log_verbosity ) {
- X log_error( "Found multiple Message-IDs! [%ld].\n",
- X (long)i );
- X }
- X }
- X } else
- X if( len == 10 && strEQ( cp, "references" ) ) {
- X /* include preceding space in saved reference */
- X len = strlen( str + 1 );
- X bcopy( str + 1, references, len + 1 );
- X str = references + len;
- X /* check for continuation lines */
- X#ifdef SERVER
- X while( get_server( cp = buff, sizeof buff ) == 0 ) {
- X#else
- X while( (cp = fgets( buff, sizeof buff, fp_article )) != Nullch ) {
- X#endif
- X if( *cp != ' ' && *cp != '\t' ) {
- X goto process_line;
- X }
- X while( *++cp == ' ' || *cp == '\t' ) {
- X ;
- X }
- X *--cp = ' ';
- X /* If the references are too long, shift them over to
- X ** always save the most recent ones.
- X */
- X if( (len += strlen( cp )) > 1023 ) {
- X strcpy( buff, buff + len - 1023 );
- X str -= len - 1023;
- X len = 1023;
- X }
- X strcpy( str, cp );
- X }/* while */
- X break;
- X }/* if */
- X }/* while */
- X if( article ) {
- X insert_article( article, date, i );
- X } else {
- X if( log_verbosity ) {
- X log_error( "Message-ID line missing! [%ld].\n", (long)i );
- X }
- X }
- X#ifndef SERVER
- X fclose( fp_article );
- X#endif
- X }
- X
- X if( extra_expire || first_article > total.first ) {
- X expire( first_article );
- X }
- X if( caught_interrupt ) {
- X return;
- X }
- X trim_roots();
- X order_roots();
- X trim_authors();
- X
- X total.first = first_article;
- X total.last = last_article;
- X#ifdef SERVER
- X extra_expire = orig_extra;
- X#endif
- X}
- X
- X/* Search all articles for numbers less than new_first. Traverse the list
- X** using the domain links so we don't have to deal with the tree structure.
- X** If extra_expire is true, stat() all valid articles to make sure they are
- X** really there and expire them if they're not.
- X*/
- Xvoid
- Xexpire( new_first )
- XART_NUM new_first;
- X{
- X register DOMAIN *domain;
- X register ARTICLE *article, *next_art, *hold;
- X
- X for( domain = &unk_domain; domain; domain = next_domain ) {
- X next_domain = domain->link;
- X for( article = domain->ids; article; article = next_art ) {
- X if( caught_interrupt ) {
- X return;
- X }
- X next_art = article->id_link;
- X if( !article->subject || (article->flags & NEW_ARTICLE) ) {
- X continue;
- X }
- X if( extra_expire && article->num >= new_first ) {
- X#ifdef SERVER
- X sprintf( buff, "STAT %ld", (long)article->num );
- X put_server( buff );
- X if( get_server( buff, sizeof buff ) == 0 && *buff == CHAR_OK ) {
- X continue;
- X }
- X#else
- X sprintf( buff, "%ld", (long)article->num );
- X if( !stat( buff, &filestat ) || errno != ENOENT ) {
- X continue;
- X }
- X#endif
- X }
- X if( extra_expire || article->num < new_first ) {
- X article->subject->count--;
- X article->subject = 0;
- X article->author->count--;
- X article->author = 0;
- X /* Free expired article if it has no children. Then check
- X ** if the parent(s) are also fake and can be freed. We'll
- X ** free any empty roots later.
- X */
- X while( !article->children ) {
- X hold = article->parent;
- X unlink_child( article );
- X free_article( article );
- X if( hold && !hold->subject ) {
- X if( (article = hold) == next_art ) {
- X next_art = next_art->id_link;
- X }
- X } else {
- X break;
- X }
- X }
- X expired_count++;
- X }/* if */
- X }/* for */
- X }/* for */
- X next_domain = Null(DOMAIN*);
- X}
- X
- X/* Trim the article chains down so that we don't have more than one faked
- X** article between the root any real ones.
- X*/
- Xvoid
- Xtrim_roots()
- X{
- X register ROOT *root, *last_root;
- X register ARTICLE *article, *next;
- X register SUBJECT *subject, *last_subj;
- X register int found;
- X
- X#ifndef lint
- X last_root = (ROOT *)&root_root;
- X#else
- X last_root = Null(ROOT*);
- X#endif
- X for( root = root_root; root; root = last_root->link ) {
- X for( article = root->articles; article; article = article->siblings ) {
- X /* If an article has no subject, it is a "fake" reference node.
- X ** If all of its immediate children are also fakes, delete it
- X ** and graduate the children to the root. If everyone is fake,
- X ** the chain dies.
- X */
- X while( !article->subject ) {
- X found = 0;
- X for( next = article->children; next; next = next->siblings ) {
- X if( next->subject ) {
- X found = 1;
- X break;
- X }
- X }
- X if( !found ) {
- X /* Remove this faked article and move all its children
- X ** up to the root.
- X */
- X next = article->children;
- X unlink_child( article );
- X free_article( article );
- X for( article = next; article; article = next ) {
- X next = article->siblings;
- X article->parent = Nullart;
- X link_child( article );
- X }
- X article = root->articles; /* start this root over */
- X } else {
- X break; /* else, on to next article */
- X }
- X }
- X }
- X /* Free all unused subject strings. Begin by trying to find a
- X ** subject for the root's pointer.
- X */
- X for( subject = root->subjects; subject && !subject->count; subject = root->subjects ) {
- X root->subjects = subject->link;
- X free_subject( subject );
- X root->subject_cnt--;
- X }
- X /* Then free up any unsed intermediate subjects.
- X */
- X if( (last_subj = subject) != Null(SUBJECT*) ) {
- X while( (subject = subject->link) != Null(SUBJECT*) ) {
- X if( !subject->count ) {
- X last_subj->link = subject->link;
- X free_subject( subject );
- X root->subject_cnt--;
- X subject = last_subj;
- X } else {
- X last_subj = subject;
- X }
- X }
- X }
- X /* Now, free all roots without articles. Flag unexpeced errors.
- X */
- X if( !root->articles ) {
- X if( root->subjects ) {
- X log_error( "** Empty root still had subjects remaining! **\n" );
- X }
- X last_root->link = root->link;
- X free_root( root );
- X } else {
- X last_root = root;
- X }
- X }
- X}
- X
- X/* Descend the author list, find any author names that aren't used
- X** anymore and free them.
- X*/
- Xvoid
- Xtrim_authors()
- X{
- X register AUTHOR *author, *last_author;
- X
- X#ifndef lint
- X last_author = (AUTHOR *)&author_root;
- X#else
- X last_author = Null(AUTHOR*);
- X#endif
- X for( author = author_root; author; author = last_author->link ) {
- X if( !author->count ) {
- X last_author->link = author->link;
- X free_author( author );
- X } else {
- X last_author = author;
- X }
- X }
- X}
- X
- X/* Reorder the roots to place the oldest ones first (age determined by
- X** date of oldest article).
- X*/
- Xvoid
- Xorder_roots()
- X{
- X register ROOT *root, *next, *search;
- X
- X /* If we don't have at least two roots, we're done! */
- X if( !(root = root_root) || !(next = root->link) ) {
- X return; /* RETURN */
- X }
- X /* Break the old list off after the first root, and then start
- X ** inserting the roots into the list by date.
- X */
- X root->link = Null(ROOT*);
- X while( (root = next) != Null(ROOT*) ) {
- X next = next->link;
- X if( (search = root_root)->articles->date >= root->articles->date ) {
- X root->link = root_root;
- X root_root = root;
- X } else {
- X while( search->link
- X && search->link->articles->date < root->articles->date ) {
- X search = search->link;
- X }
- X root->link = search->link;
- X search->link = root;
- X }
- X }
- X}
- X
- X#define EQ(x,y) ((isupper(x) ? tolower(x) : (x)) == (y))
- X
- X/* Parse the subject into 72 characters or less. Remove any "Re[:^]"s from
- X** the front (noting that it's there), and any "(was: old)" stuff from
- X** the end. Then, compact multiple whitespace characters into one space,
- X** trimming leading/trailing whitespace. If it's still too long, unmercifully
- X** cut it off. We don't bother with subject continuation lines either.
- X*/
- Xvoid
- Xget_subject_str( str )
- Xregister char *str;
- X{
- X register char *cp;
- X register int len;
- X
- X while( *str && (unsigned char)*str <= ' ' ) {
- X str++;
- X }
- X if( !*str ) {
- X bcopy( "<None>", subject_str, 7 );
- X return; /* RETURN */
- X }
- X cp = str;
- X while( EQ( cp[0], 'r' ) && EQ( cp[1], 'e' ) ) { /* check for Re: */
- X cp += 2;
- X if( *cp == '^' ) { /* allow Re^2: */
- X while( *++cp <= '9' && *cp >= '0' ) {
- X ;
- X }
- X }
- X if( *cp != ':' ) {
- X break;
- X }
- X while( *++cp == ' ' ) {
- X ;
- X }
- X found_Re = 1;
- X str = cp;
- X }
- X /* Remove "(was Re: oldsubject)", because we already know the old subjects.
- X ** Also match "(Re: oldsubject)". Allow possible spaces after the ('s.
- X */
- X for( cp = str; (cp = index( cp+1, '(' )) != Nullch; ) {
- X while( *++cp == ' ' ) {
- X ;
- X }
- X if( EQ( cp[0], 'w' ) && EQ( cp[1], 'a' ) && EQ( cp[2], 's' )
- X && (cp[3] == ':' || cp[3] == ' ') )
- X {
- X *--cp = '\0';
- X break;
- X }
- X if( EQ( cp[0], 'r' ) && EQ( cp[1], 'e' )
- X && ((cp[2]==':' && cp[3]==' ') || (cp[2]=='^' && cp[4]==':')) ) {
- X *--cp = '\0';
- X break;
- X }
- X }
- X /* Copy subject to a temporary string, compacting multiple spaces/tabs */
- X for( len = 0, cp = subject_str; len < 72 && *str; len++ ) {
- X if( (unsigned char)*str <= ' ' ) {
- X while( *++str && (unsigned char)*str <= ' ' ) {
- X ;
- X }
- X *cp++ = ' ';
- X } else {
- X *cp++ = *str++;
- X }
- X }
- X if( cp[-1] == ' ' ) {
- X cp--;
- X }
- X *cp = '\0';
- X}
- X
- X/* Try to fit the author name in 16 bytes. Use the comment portion in
- X** parenthesis if present. Cut off non-commented names at the '@' or '%'.
- X** Then, put as many characters as we can into the 16 bytes, packing multiple
- X** whitespace characters into a single space.
- X** We should really implement a nice name shortening algorithm, or simply
- X** grab the name packing code from nn.
- X*/
- Xvoid
- Xget_author_str( str )
- Xchar *str;
- X{
- X register char *cp, *cp2;
- X
- X if( (cp = index( str, '(' )) != Nullch ) {
- X str = cp+1;
- X if( (cp = rindex( str, ')' )) != Nullch ) {
- X *cp = '\0';
- X }
- X } else {
- X if( (cp = index( str, '@' )) != Nullch ) {
- X *cp = '\0';
- X }
- X if( (cp = index( str, '%' )) != Nullch ) {
- X *cp = '\0';
- X }
- X }
- X for( cp = str, cp2 = author_str; *cp && cp2-author_str < 16; ) {
- X /* Pack white space and turn ctrl-chars into spaces. */
- X if( *cp <= ' ' ) {
- X while( *++cp && *cp <= ' ' ) {
- X ;
- X }
- X if( cp2 != author_str ) {
- X *cp2++ = ' ';
- X }
- X } else {
- X *cp2++ = *cp++;
- X }
- X }
- X *cp2 = '\0';
- X}
- X
- X/* Take a message-id and see if we already know about it. If so, return it.
- X** If not, create it. We separate the id into its id@domain parts, and
- X** link all the unique ids to one copy of the domain portion. This saves
- X** a bit of space.
- X*/
- XARTICLE *
- Xget_article( msg_id )
- Xchar *msg_id;
- X{
- X register DOMAIN *domain;
- X register ARTICLE *article;
- X register char *cp, *after_at;
- X
- X /* Take message id, break it up into <id@domain>, and try to match it.
- X */
- X while( *msg_id == ' ' ) {
- X msg_id++;
- X }
- X cp = msg_id + strlen( msg_id ) - 1;
- X if( msg_id >= cp ) {
- X if( log_verbosity ) {
- X log_error( "Message-ID is empty!\n" );
- X }
- X return Nullart;
- X }
- X if( *msg_id++ != '<' ) {
- X if( log_verbosity ) {
- X log_error( "Message-ID doesn't start with '<'.\n" );
- X }
- X msg_id--;
- X }
- X if( *cp != '>' ) {
- X if( log_verbosity ) {
- X log_error( "Message-ID doesn't end with '>'.\n" );
- X }
- X cp++;
- X }
- X *cp = '\0';
- X if( msg_id == cp ) {
- X if( log_verbosity ) {
- X log_error( "Message-ID is null!\n" );
- X }
- X return Nullart;
- X }
- X
- X if( (after_at = index( msg_id, '@' )) == Nullch ) {
- X domain = &unk_domain;
- X } else {
- X *after_at++ = '\0';
- X for( cp = after_at; *cp; cp++ ) {
- X if( isupper(*cp) ) {
- X *cp = tolower(*cp); /* lower-case domain portion */
- X }
- X }
- X *cp = '\0';
- X /* Try to find domain name in database. */
- X for( domain = unk_domain.link; domain; domain = domain->link ) {
- X if( strEQ( domain->name, after_at ) ) {
- X break;
- X }
- X }
- X if( !domain ) { /* if domain doesn't exist, create it */
- X register int len = cp - after_at + 1;
- X domain = (DOMAIN *)safemalloc( sizeof (DOMAIN) );
- X total.domain++;
- X domain->name = safemalloc( len );
- X total.string2 += len;
- X bcopy( after_at, domain->name, len );
- X domain->ids = Nullart;
- X domain->link = unk_domain.link;
- X unk_domain.link = domain;
- X }
- X }
- X /* Try to find id in this domain. */
- X for( article = domain->ids; article; article = article->id_link ) {
- X if( strEQ( article->id, msg_id ) ) {
- X break;
- X }
- X }
- X if( !article ) { /* If it doesn't exist, create an article */
- X register int len = strlen( msg_id ) + 1;
- X article = (ARTICLE *)safemalloc( sizeof (ARTICLE) );
- X bzero( article, sizeof (ARTICLE) );
- X total.article++;
- X article->num = 0;
- X article->id = safemalloc( len );
- X total.string2 += len;
- X bcopy( msg_id, article->id, len );
- X article->domain = domain;
- X article->id_link = domain->ids;
- X domain->ids = article;
- X }
- X return article;
- X}
- X
- X/* Take all the data we've accumulated about the article and shove it into
- X** the article tree at the best place we can possibly imagine.
- X*/
- Xvoid
- Xinsert_article( article, date, num )
- XARTICLE *article;
- Xtime_t date;
- XART_NUM num;
- X{
- X register ARTICLE *node, *last;
- X register char *cp, *end;
- X int len;
- X
- X if( article->subject ) {
- X if( log_verbosity ) {
- X log_error( "We've already seen article #%ld (%s@%s)\n",
- X (long)num, article->id, article->domain->name );
- X }
- X return; /* RETURN */
- X }
- X article->date = date;
- X article->num = num;
- X article->flags = NEW_ARTICLE;
- X
- X if( !*references && found_Re ) {
- X if( log_verbosity > 1 ) {
- X log_error( "Missing reference line! [%ld]\n", (long)num );
- X }
- X }
- X /* If the article has a non-zero root, it is already in a thread somewhere.
- X ** Unlink it to try to put it in the best possible spot.
- X */
- X if( article->root ) {
- X /* Check for a real or shared-fake parent. Articles that have never
- X ** existed have a num of 0. Expired articles that remain as references
- X ** have a valid num. (Valid date too, but no subject.)
- X */
- X for( node = article->parent;
- X node && !node->num && node->child_cnt == 1;
- X node = node->parent )
- X {
- X ;
- X }
- X unlink_child( article );
- X if( node ) { /* do we have decent parents? */
- X /* Yes: assume that our references are ok, and just reorder us
- X ** with our siblings by date.
- X */
- X link_child( article );
- X use_root( article, article->root );
- X /* Freshen the date in any faked parent articles. */
- X for( node = article->parent;
- X node && !node->num && date < node->date;
- X node = node->parent )
- X {
- X node->date = date;
- X unlink_child( node );
- X link_child( node );
- X }
- X return; /* RETURN */
- X }
- X /* We'll assume that this article has as good or better references
- X ** than the child that faked us initially. Free the fake reference-
- X ** chain and process our references as usual.
- X */
- X for( node = article->parent; node; node = node->parent ) {
- X unlink_child( node );
- X free_article( node );
- X }
- X article->parent = Nullart; /* neaten up */
- X article->siblings = Nullart;
- X }
- X check_references:
- X if( !*references ) { /* If no references but "Re:" in subject, */
- X if( found_Re ) { /* search for a reference in any cited text */
- X#ifndef SERVER
- X for( len = 4; len && fgets( buff, sizeof buff, fp_article ); len-- ) {
- X if( (cp = index( buff, '<' )) && (end = index( cp, ' ' )) ) {
- X if( end[-1] == ',' ) {
- X end--;
- X }
- X *end = '\0';
- X if( (end = index( cp, '>' )) == Nullch ) {
- X end = cp + strlen( cp ) - 1;
- X }
- X if( valid_message_id( cp, end ) ) {
- X strcpy( references+1, cp );
- X *references = ' ';
- X if( log_verbosity > 2 ) {
- X log_error( "Found cited-text reference: '%s' [%ld]\n",
- X references+1, (long)num );
- X }
- X break;
- X }
- X }
- X }
- X#endif
- X } else {
- X article->flags |= ROOT_ARTICLE;
- X }
- X }
- X /* If we have references, process them from the right end one at a time
- X ** until we either run into somebody, or we run out of references.
- X */
- X if( *references ) {
- X last = article;
- X node = Nullart;
- X end = references + strlen( references ) - 1;
- X while( (cp = rindex( references, ' ' )) != Nullch ) {
- X *cp++ = '\0';
- X while( end >= cp && ((unsigned char)*end <= ' ' || *end == ',') ) {
- X end--;
- X }
- X end[1] = '\0';
- X /* Quit parsing references if this one is garbage. */
- X if( !valid_message_id( cp, end ) ) {
- X if( log_verbosity ) {
- X log_error( "Bad ref '%s' [%ld]\n", cp, (long)num );
- X }
- X break;
- X }
- X /* Dump all domains that end in '.', such as "..." & "1@DEL." */
- X if( end[-1] == '.' ) {
- X break;
- X }
- X node = get_article( cp );
- X /* Check for duplicates on the reference line. Brand-new data has
- X ** no date. Data we just allocated earlier on this line has a
- X ** date but no root. Special-case the article itself, since it
- X ** MIGHT have a root.
- X */
- X if( (node->date && !node->root) || node == article ) {
- X if( log_verbosity ) {
- X log_error( "Reference line contains duplicates [%ld]\n",
- X (long)num );
- X }
- X if( (node = last) == article ) {
- X node = Nullart;
- X }
- X continue;
- X }
- X last->parent = node;
- X link_child( last );
- X if( node->root ) {
- X break;
- X }
- X node->date = date;
- X last = node;
- X end = cp-2;
- X }
- X if( !node ) {
- X *references = '\0';
- X goto check_references;
- X }
- X /* Check if we ran into anybody that was already linked. If so, we
- X ** just use their root.
- X */
- X if( node->root ) {
- X /* See if this article spans the gap between what we thought
- X ** were two different roots.
- X */
- X if( article->root && article->root != node->root ) {
- X merge_roots( node->root, article->root );
- X /* Set the roots of any children we brought with us. */
- X set_root( article, node->root );
- X }
- X use_root( article, node->root );
- X } else {
- X /* We didn't find anybody we knew, so either create a new root or
- X ** use the article's root if it was previously faked.
- X */
- X if( !article->root ) {
- X make_root( node );
- X use_root( article, node->root );
- X } else {
- X use_root( article, article->root );
- X node->root = article->root;
- X link_child( node );
- X }
- X }
- X /* Set the roots of the faked articles we created as references. */
- X for( node = article->parent; node && !node->root; node = node->parent ) {
- X node->root = article->root;
- X }
- X /* Make sure we didn't circularly link to a child article(!), by
- X ** ensuring that we run into the root before we run into ourself.
- X */
- X while( node && node->parent != article ) {
- X node = node->parent;
- X }
- X if( node ) {
- X /* Ugh. Someone's tweaked reference line with an incorrect
- X ** article order arrived first, and one of our children is
- X ** really one of our ancestors. Cut off the bogus child branch
- X ** right where we are and link it to the root.
- X */
- X if( log_verbosity ) {
- X log_error("Found ancestral child -- fixing.\n");
- X }
- X unlink_child( node );
- X node->parent = Nullart;
- X link_child( node );
- X }
- X } else {
- X /* The article has no references. Either turn it into a new root, or
- X ** re-attach fleshed-out (previously faked) article to its old root.
- X */
- X if( !article->root ) {
- X make_root( article );
- X } else {
- X use_root( article, article->root );
- X link_child( article );
- X }
- X }
- X}
- X
- X/* Check if the string we've found looks like a valid message-id reference.
- X*/
- Xint
- Xvalid_message_id( start, end )
- Xregister char *start, *end;
- X{
- X char *mid;
- X
- X if( *end != '>' ) {
- X /* Compensate for spacecadets who include the header in their
- X ** subsitution of all '>'s into another citation character.
- X */
- X if( *end == '<' || *end == '-' || *end == '!' || *end == '%'
- X || *end == ')' || *end == '|' || *end == ':' || *end == '}'
- X || *end == '*' || *end == '+' || *end == '#' || *end == ']'
- X || *end == '@' ) {
- X if( log_verbosity ) {
- X log_error( "Reference ended in '%c'.\n", *end );
- X }
- X *end = '>';
- X }
- X }
- X /* Id must be "<...@...>" */
- X if( *start != '<' || *end != '>' || (mid = index( start, '@' )) == Nullch
- X || mid == start+1 || mid+1 == end ) {
- X return 0; /* RETURN */
- X }
- X return 1;
- X}
- X
- X/* Remove an article from its parent/siblings. Leave parent pointer intact.
- X*/
- Xvoid
- Xunlink_child( child )
- Xregister ARTICLE *child;
- X{
- X register ARTICLE *last;
- X
- X if( !(last = child->parent) ) {
- X child->root->thread_cnt--;
- X if( (last = child->root->articles) == child ) {
- X child->root->articles = child->siblings;
- X } else {
- X goto sibling_search;
- X }
- X } else {
- X last->child_cnt--;
- X if( last->children == child ) {
- X last->children = child->siblings;
- X } else {
- X last = last->children;
- X sibling_search:
- X while( last->siblings != child ) {
- X last = last->siblings;
- X }
- X last->siblings = child->siblings;
- X }
- X }
- X}
- X
- X/* Link an article to its parent article. If its parent pointer is zero,
- X** link it to its root. Sorts siblings by date.
- X*/
- Xvoid
- Xlink_child( child )
- Xregister ARTICLE *child;
- X{
- X register ARTICLE *node;
- X register ROOT *root;
- X
- X if( !(node = child->parent) ) {
- X root = child->root;
- X root->thread_cnt++;
- X node = root->articles;
- X if( !node || child->date < node->date ) {
- X child->siblings = node;
- X root->articles = child;
- X } else {
- X goto sibling_search;
- X }
- X } else {
- X node->child_cnt++;
- X node = node->children;
- X if( !node || child->date < node->date ) {
- X child->siblings = node;
- X child->parent->children = child;
- X } else {
- X sibling_search:
- X for( ; node->siblings; node = node->siblings ) {
- X if( node->siblings->date > child->date ) {
- X break;
- X }
- X }
- X child->siblings = node->siblings;
- X node->siblings = child;
- X }
- X }
- X}
- X
- X/* Create a new root for the specified article. If the current subject_str
- X** matches any pre-existing root's subjects, we'll instead add it on as a
- X** parallel thread.
- X*/
- Xvoid
- Xmake_root( article )
- XARTICLE *article;
- X{
- X register ROOT *new, *node;
- X register SUBJECT *subject;
- X
- X#ifndef NO_SUBJECT_MATCHING
- X /* First, check the other root's subjects for a match. */
- X for( node = root_root; node; node = node->link ) {
- X for( subject = node->subjects; subject; subject = subject->link ) {
- X if( subject_equal( subject->str, subject_str ) ) {
- X use_root( article, node ); /* use it instead */
- X link_child( article );
- X return; /* RETURN */
- X }
- X }
- X }
- X#endif
- X
- X /* Create a new root. */
- X new = (ROOT *)safemalloc( sizeof (ROOT) );
- X total.root++;
- X new->articles = article;
- X new->root_num = article->num;
- X new->thread_cnt = 1;
- X if( article->num ) {
- X article->author = new_author();
- X new->subject_cnt = 1;
- X new->subjects = article->subject = new_subject();
- X } else {
- X new->subject_cnt = 0;
- X new->subjects = Null(SUBJECT*);
- X }
- X article->root = new;
- X new->link = root_root;
- X root_root = new;
- X}
- X
- X/* Add this article's subject onto the indicated root's list. Point the
- X** article at the root.
- X*/
- Xvoid
- Xuse_root( article, root )
- XARTICLE *article;
- XROOT *root;
- X{
- X register SUBJECT *subject;
- X register ROOT *root2;
- X SUBJECT *hold, *child_subj = Null(SUBJECT*);
- X ARTICLE *node;
- X
- X article->root = root;
- X
- X /* If it's a fake, there's no subject to add. */
- X if( !article->num ) {
- X return; /* RETURN */
- X }
- X
- X /* If we haven't picked a unique message number to represent this root,
- X ** use the first non-zero number we encounter. Which one doesn't matter.
- X */
- X if( !root->root_num ) {
- X root->root_num = article->num;
- X }
- X article->author = new_author();
- X
- X /* Check if the new subject matches any of the other subjects in this root.
- X ** If so, we just update the count. If not, check all the other roots for
- X ** a match. If found, the new subject is common between the two roots, so
- X ** we merge the two roots together.
- X */
- X root2 = root;
- X#ifndef NO_SUBJECT_MATCHING
- X do {
- X#endif
- X for( subject = root2->subjects; subject; subject = subject->link ) {
- X if( subject_equal( subject->str, subject_str ) ) {
- X article->subject = subject;
- X subject->count++;
- X#ifndef NO_SUBJECT_MATCHING
- X if( root2 != root ) {
- X merge_roots( root, root2 );
- X }
- X#endif
- X return; /* RETURN */
- X }
- X }
- X#ifndef NO_SUBJECT_MATCHING
- X if( (root2 = root2->link) == Null(ROOT*) ) {
- X root2 = root_root;
- X }
- X } while( root2 != root );
- X#endif
- X
- X article->subject = hold = new_subject();
- X root->subject_cnt++;
- X
- X /* Find subject of any pre-existing children. We want to insert the new
- X ** subject before a child's to keep the subject numbering intuitive
- X ** in the newsreader.
- X */
- X for( node = article->children; node; node = node->children ) {
- X if( node->subject ) {
- X child_subj = node->subject;
- X break;
- X }
- X }
- X if( !(subject = root->subjects) || subject == child_subj ) {
- X hold->link = root->subjects;
- X root->subjects = hold;
- X } else {
- X while( subject->link && subject->link != child_subj ) {
- X subject = subject->link;
- X }
- X hold->link = subject->link;
- X subject->link = hold;
- X }
- X}
- X
- X/* Check subjects in a case-insignificant, punctuation ignoring manner.
- X*/
- Xint
- Xsubject_equal( str1, str2 )
- Xregister char *str1, *str2;
- X{
- X register char ch1, ch2;
- X
- X while( (ch1 = *str1++) ) {
- X if( ch1 == ' ' || ispunct( ch1 ) ) {
- X while( *str1 && (*str1 == ' ' || ispunct( *str1 )) ) {
- X str1++;
- X }
- X ch1 = ' ';
- X } else if( isupper( ch1 ) ) {
- X ch1 = tolower( ch1 );
- X }
- X if( !(ch2 = *str2++) ) {
- X return 0;
- X }
- X if( ch2 == ' ' || ispunct( ch2 ) ) {
- X while( *str2 && (*str2 == ' ' || ispunct( *str2 )) ) {
- X str2++;
- X }
- X ch2 = ' ';
- X } else if( isupper( ch2 ) ) {
- X ch2 = tolower( ch2 );
- X }
- X if( ch1 != ch2 ) {
- X return 0;
- X }
- X }
- X if( *str2 ) {
- X return 0;
- X }
- X return 1;
- X}
- X
- X/* Create a new subject structure. */
- XSUBJECT *
- Xnew_subject()
- X{
- X register int len = strlen( subject_str ) + 1;
- X register SUBJECT *subject;
- X
- X subject = (SUBJECT *)safemalloc( sizeof (SUBJECT) );
- X total.subject++;
- X subject->count = 1;
- X subject->link = Null(SUBJECT*);
- X subject->str = safemalloc( len );
- X total.string1 += len;
- X bcopy( subject_str, subject->str, len );
- X
- X return subject;
- X}
- X
- X/* Create a new author structure. */
- XAUTHOR *
- Xnew_author()
- X{
- X register len = strlen( author_str ) + 1;
- X register AUTHOR *author, *last_author;
- X
- X last_author = Null(AUTHOR*);
- X for( author = author_root; author; author = author->link ) {
- X#ifndef DONT_COMPARE_AUTHORS /* might like to define this to save time */
- X if( strEQ( author->name, author_str ) ) {
- X author->count++;
- X return author; /* RETURN */
- X }
- X#endif
- X last_author = author;
- X }
- X
- X author = (AUTHOR *)safemalloc( sizeof (AUTHOR) );
- X total.author++;
- X author->count = 1;
- X author->link = Null(AUTHOR*);
- X author->name = safemalloc( len );
- X total.string1 += len;
- X bcopy( author_str, author->name, len );
- X
- X if( last_author ) {
- X last_author->link = author;
- X } else {
- X author_root = author;
- X }
- X return author;
- X}
- X
- X/* Insert all of root2 into root1, setting the proper root values and
- X** updating subject counts.
- X*/
- Xvoid
- Xmerge_roots( root1, root2 )
- XROOT *root1, *root2;
- X{
- X register ARTICLE *node, *next;
- X register SUBJECT *subject;
- X
- X /* Remember whoever's root num is lower. This could screw up a
- X ** newsreader's kill-thread code if someone already saw the roots as
- X ** being separate, but it must be done. The newsreader code will have
- X ** to handle this as best as it can.
- X */
- X if( root1->root_num > root2->root_num ) {
- X root1->root_num = root2->root_num;
- X }
- X
- X for( node = root2->articles; node; node = next ) {
- X /* For each article attached to root2, detach them, set the
- X ** branch's root pointers to root1, and then attach it to root1.
- X */
- X next = node->siblings;
- X unlink_child( node );
- X node->siblings = Nullart;
- X set_root( node, root1 ); /* sets children too */
- X /* Link_child() depends on node->parent being null and node->root
- X ** being set.
- X */
- X link_child( node );
- X }
- X root1->subject_cnt += root2->subject_cnt;
- X if( !(subject = root1->subjects) ) {
- X root1->subjects = root2->subjects;
- X } else {
- X while( subject->link ) {
- X subject = subject->link;
- X }
- X subject->link = root2->subjects;
- X }
- X unlink_root( root2 );
- X free_root( root2 );
- X}
- X
- X/* When merging roots, we need to reset all the root pointers.
- X*/
- Xvoid
- Xset_root( node, root )
- XARTICLE *node;
- XROOT *root;
- X{
- X do {
- X node->root = root;
- X if( node->children ) {
- X set_root( node->children, root );
- X }
- X } while( node = node->siblings );
- X}
- X
- X/* Unlink a root from its neighbors. */
- Xvoid
- Xunlink_root( root )
- Xregister ROOT *root;
- X{
- X register ROOT *node;
- X
- X if( (node = root_root) == root ) {
- X root_root = root->link;
- X } else {
- X while( node->link != root ) {
- X node = node->link;
- X }
- X node->link = root->link;
- X }
- X}
- X
- X/* Free an article and its message-id string. All other resources must
- X** already be free, and it must not be attached to any threads.
- X*/
- Xvoid
- Xfree_article( this )
- XARTICLE *this;
- X{
- X register ARTICLE *art;
- X
- X if( (art = this->domain->ids) == this ) {
- X if( !(this->domain->ids = this->id_link) ) {
- X free_domain( this->domain );
- X }
- X } else {
- X while( this != art->id_link ) {
- X art = art->id_link;
- X }
- X art->id_link = this->id_link;
- X }
- X total.string2 -= strlen( this->id ) + 1;
- X free( this->id );
- X free( this );
- X total.article--;
- X}
- X
- X/* Free the domain only when its last unique id has been freed. */
- Xvoid
- Xfree_domain( this )
- XDOMAIN *this;
- X{
- X register DOMAIN *domain;
- X
- X if( this == (domain = &unk_domain) ) {
- X return;
- X }
- X if( this == next_domain ) { /* help expire routine skip freed domains */
- X next_domain = next_domain->link;
- X }
- X while( this != domain->link ) {
- X domain = domain->link;
- X }
- X domain->link = this->link;
- X total.string2 -= strlen( this->name ) + 1;
- X free( this->name );
- X free( this );
- X total.domain--;
- X}
- X
- X/* Free the subject structure and its string. */
- Xvoid
- Xfree_subject( this )
- XSUBJECT *this;
- X{
- X total.string1 -= strlen( this->str ) + 1;
- X free( this->str );
- X free( this );
- X total.subject--;
- X}
- X
- X/* Free a root. It must already be unlinked. */
- Xvoid
- Xfree_root( this )
- XROOT *this;
- X{
- X free( this );
- X total.root--;
- X}
- X
- X/* Free the author structure when it's not needed any more. */
- Xvoid
- Xfree_author( this )
- XAUTHOR *this;
- X{
- X total.string1 -= strlen( this->name ) + 1;
- X free( this->name );
- X free( this );
- X total.author--;
- X}
- END_OF_FILE
- if test 34306 -ne `wc -c <'mt-process.c'`; then
- echo shar: \"'mt-process.c'\" unpacked with wrong size!
- fi
- # end of 'mt-process.c'
- fi
- echo shar: End of archive 5 \(of 14\).
- cp /dev/null ark5isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 14 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
-